#include <iostream>
#include <curl/curl.h>
#include <vector>
#include <string>
#include "RingBuffer.h"
#include "EventCallback.h"

class Downloader {
public:
    Downloader(RingBuffer &buffer, std::vector<std::string> &urls)
        : buffer_(buffer)
        , urls_(urls)
        , stop_(false)
        , callback_(nullptr)
    {
    }

    void Start()
    {
        worker_ = std::thread(&Downloader::DownloadThread, this);
    }

    void Stop()
    {
        stop_ = true;
        if (worker_.joinable())
            worker_.join();
    }

    void SetEventCallback(EventCallback *callback)
    {
        callback_ = callback;
    }

private:
    struct ProgressData
    {
        std::string url;
        size_t total_size;
        size_t downloaded;
        Downloader *self;
    };

    static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
    {
        ProgressData *progress = static_cast<ProgressData *>(userp);
        size_t total = size * nmemb;
        progress->downloaded += total;

        Downloader *self = progress->self;
        if (self->callback_) {
            self->callback_->OnDownloadProgress(progress->url, progress->downloaded, progress->total_size);
        }

        while (total > 0 && !self->stop_) {
            size_t chunk = std::min(total, self->buffer_.GetFreeSpace());
            if (chunk == 0) {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                continue;
            }

            if (self->buffer_.Write(static_cast<uint8_t *>(contents), chunk)) {
                contents = static_cast<uint8_t *>(contents) + chunk;
                total -= chunk;
            } else {
                std::this_thread::yield();
            }
        }
        return size * nmemb - total;
    }

    static size_t HeaderCallback(void *ptr, size_t size, size_t nmemb, void *userdata)
    {
        ProgressData *progress = static_cast<ProgressData *>(userdata);
        size_t total = size * nmemb;
        std::string header(static_cast<char *>(ptr), total);

        // 提取内容长度
        if (header.find("Content-Length:") != std::string::npos) {
            size_t pos = header.find(": ");
            if (pos != std::string::npos) {
                progress->total_size = std::stoul(header.substr(pos + 2));
                std::cout << "total_size: " << progress->total_size << std::endl;
            }
        }
        return total;
    }

    void DownloadThread()
    {
        CURL *curl = curl_easy_init();
        if (!curl)
            return;

        for (const auto &url : urls_) {
            if (stop_)
                break;

            if (callback_)
                callback_->OnDownloadStart(url);

            ProgressData progress{url, 0, 0, this};

            curl_easy_setopt(curl, CURLOPT_HEADER, 1); // 需要header头
            curl_easy_setopt(curl, CURLOPT_NOBODY, 0); // 不需要body
            curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &progress);
            curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
            curl_easy_setopt(curl, CURLOPT_HEADERDATA, &progress);
            curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
            curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // if want to use https
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); // set peer and host verify false
            curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); // 禁用CURL进度

            CURLcode res = curl_easy_perform(curl);
            if (res != CURLE_OK) {
                if (callback_)
                    callback_->OnDownloadError(url, curl_easy_strerror(res));
            } else {
                if (callback_)
                    callback_->OnDownloadComplete(url);
            }
        }
        curl_easy_cleanup(curl);
    }

    RingBuffer &buffer_;
    std::vector<std::string> &urls_;
    std::thread worker_;
    std::atomic<bool> stop_;
    EventCallback *callback_;
};